package cn.dansj.common.utils.spring;

import cn.dansj.common.utils.spring.context.ApplicationContextProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.AnnotationUtils;

import javax.annotation.Resource;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;

public abstract class DependencyInjectionUtils {
    private static final Logger logger = LoggerFactory.getLogger(DependencyInjectionUtils.class);

    /**
     * 实例化类并自动注入构造参数
     *
     * @param clazz 要实例化的类
     * @return 实例化后的对象
     * @throws Exception 如果实例化失败
     */
    @SuppressWarnings("unchecked")
    public static <T> T newInstance(Class<T> clazz) throws Exception {
        // 获取所有构造方法
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        // 选择构造方法
        Constructor<?> constructor = selectConstructor(constructors);
        if (constructor == null) {
            throw new IllegalStateException("No suitable constructor found for class: " + clazz.getName());
        }
        // 解析构造参数
        Object[] args = resolveConstructorParameters(constructor);
        // 实例化对象
        return (T) injectService(constructor.newInstance(args));
    }

    /**
     * 选择构造方法
     *
     * @param constructors 所有构造方法
     * @return 选择的构造方法
     */
    private static Constructor<?> selectConstructor(Constructor<?>[] constructors) {
        // 优先选择 @Autowired 标注的构造方法
        for (Constructor<?> constructor : constructors) {
            if (constructor.isAnnotationPresent(Autowired.class)) {
                return constructor;
            }
        }

        // 如果没有 @Autowired 标注，选择参数数量最多的构造方法
        if (constructors.length > 0) {
            Arrays.sort(constructors, (c1, c2) -> Integer.compare(c2.getParameterCount(), c1.getParameterCount()));
            return constructors[0];
        }

        return null;
    }

    /**
     * 解析构造方法的参数
     *
     * @param constructor 构造方法
     * @return 参数数组
     * @throws IllegalStateException 如果依赖未找到
     */
    private static Object[] resolveConstructorParameters(Constructor<?> constructor) {
        Class<?>[] parameterTypes = constructor.getParameterTypes();
        Object[] args = new Object[parameterTypes.length];

        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> paramType = parameterTypes[i];
            if (!ApplicationContextProvider.hasBean(paramType)) {
                throw new IllegalStateException("Dependency not found for type: " + paramType.getName());
            }
            args[i] = ApplicationContextProvider.getBean(paramType);
        }

        return args;
    }

    /**
     * inject service of bean field
     */
    public static <T> T injectService(T instance) {
        if (instance == null) {
            return null;
        }

        if (ApplicationContextProvider.getApplicationContext() == null) {
            return instance;
        }

        Field[] fields = instance.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }

            Object fieldBean = null;
            // with bean-id, bean could be found by both @Resource and @Autowired, or bean could only be found by @Autowired
            if (AnnotationUtils.getAnnotation(field, Resource.class) != null) {
                try {
                    Resource resource = AnnotationUtils.getAnnotation(field, Resource.class);
                    assert resource != null;
                    if (resource.name() != null && !resource.name().isEmpty()) {
                        fieldBean = ApplicationContextProvider.getBean(resource.name());
                    } else {
                        fieldBean = ApplicationContextProvider.getBean(field.getName());
                    }
                } catch (Exception ignored) {
                }
                if (fieldBean == null) {
                    fieldBean = ApplicationContextProvider.getBean(field.getType());
                }
            } else if (AnnotationUtils.getAnnotation(field, Autowired.class) != null) {
                Qualifier qualifier = AnnotationUtils.getAnnotation(field, Qualifier.class);
                if (qualifier != null && !qualifier.value().isEmpty()) {
                    fieldBean = ApplicationContextProvider.getBean(qualifier.value());
                } else {
                    fieldBean = ApplicationContextProvider.getBean(field.getType());
                }
            }

            if (fieldBean != null) {
                field.setAccessible(true);
                try {
                    field.set(instance, fieldBean);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
        return instance;
    }
}
